//jQuery扩展方法
(function ($) {
    if (!!jQuery.validator) {
        // 联系电话(手机/电话皆可)验证
        jQuery.validator.addMethod("isTel", function (value, element) {
            let length = value.length;
            let mobile = /^(((13[0-9]{1})|(14[0-9]{1})|(15[0-9]{1})|(16[0-9]{1})|(17[0-9]{1})|(18[0-9]{1})|(19[0-9]{1}))+(\d{4}|[****])+\d{4})$/;
            return this.optional(element) || (length === 11 && mobile.test(value));
        }, "请正确填写您的联系方式");
        jQuery.validator.addMethod("isTelephone", function (value, element) {
            let length = value.length;
            let mobile = /^(((13[0-9]{1})|(14[0-9]{1})|(15[0-9]{1})|(16[0-9]{1})|(17[0-9]{1})|(18[0-9]{1})|(19[0-9]{1}))+\d{8})$/;
            return this.optional(element) || (length === 11 && mobile.test(value));
        }, "请正确填写您的联系方式");
        jQuery.validator.addMethod("isTime", function (value, element) {
            let rule = /^([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/;
            return this.optional(element) || rule.test(value);
        }, "请正确填写时间");
        jQuery.validator.addMethod("isGeneralWithChinese", function (value, element) {
            let rule = /^[\u4E00-\u9FA5A-Za-z0-9()\-_m³㎡]+$/;
            let rule2 = /^[^\uFF00-\uFFFF\s]+$/;
            return this.optional(element) || rule.test(value) && rule2.test(value);
        }, "检测到非法字符，请重新输入");
        jQuery.validator.addMethod("isGeneral", function (value, element) {
            let rule = /^[\u4E00-\u9FA5A-Za-z0-9]+$/;
            let rule2 = /^[^\uFF00-\uFFFF\s]+$/;
            return this.optional(element) || rule.test(value) && rule2.test(value);
        }, "仅支持中文，英文，数字，请重新输入");
        jQuery.validator.addMethod("isHttp", function (value, element) {
            let rule = /(http|https):\/\/([\w.]+\/?)\S*/;
            return this.optional(element) || rule.test(value);
        }, "请输入正确的URL地址");
        jQuery.validator.addMethod("isPositive", function (value, element) {
            let rule = /^([1-9]\d*)|(^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$)$/;
            return this.optional(element) || rule.test(value);
        }, "请输入正数");
        jQuery.validator.addMethod("isPositiveWithZero", function (value, element) {
            let rule = /^((0{1}\.\d{1,3})|([1-9]\d*\.{1}\d{1,3})|([1-9]+\d*)|0)$/;
            return this.optional(element) || rule.test(value);
        }, "请输入0以上的正数");
        jQuery.validator.addMethod("isDecimalFormat", function (value, element, param) {
            let rule = new RegExp(eval(`/(^[0-9]{1,${param[0]}}$)|(^[0-9]{1,${param[0]}}[\.]{1}[0-9]{1,${param[1]}}$)/`));
            return this.optional(element) || rule.test(value);
        }, $.validator.format("请输入{0}位以内的正整数或{0}位以内正整数和{1}位以内小数"));
        jQuery.validator.addMethod("isNumberAndZeroToOne", function (value, element) {
            let rule = /^[0-9]+(\.?[0-9]+)?$/;
            return this.optional(element) || rule.test(value) && (parseFloat(value) >= 0 && parseFloat(value) <= 1);
        }, "请输入0到1之间的数字");
        jQuery.validator.addMethod("isStrongPassword", function (value, element) {
            let rule = /^(?=.{12,20})(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[?!`<>()@#$%^&+=]).*$/;
            let rule2 = /^[^\uFF00-\uFFFF\s]+$/;
            return this.optional(element) || rule.test(value) && rule2.test(value);
        }, "密码长度必须在12到20之间且包含数字、大小写字母和特殊字符");
        jQuery.validator.addMethod("isEmail", function (value, element) {
            let rule = /^(?:[a-zA-Z0-9!#$%*&'+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%*&'+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-zA-Z0-9-]*[a-zA-Z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)])+$/;
            return this.optional(element) || rule.test(value);
        }, "请输入正确的邮箱");
    }

    $.fn.extend({
        //遍历select 增加option
        addOptionsWithKeyValue: function (keyValuePairs, clear) {
            if (clear) {
                $(this).html('');
            }
            for (let key in keyValuePairs) {
                $(this).append(
                    '<option value="' + keyValuePairs[key].id + '">' + keyValuePairs[key].name + '</option>'
                );
            }
        },
        //序列化函数
        serializeObject: function () {
            let o = {};
            let a = this.serializeArray();
            $.each(a, function () {
                if (o[this.name]) {
                    if (!o[this.name].push) {
                        o[this.name] = [o[this.name]];
                    }
                    o[this.name].push(this.value.trim() || '');
                } else {
                    o[this.name] = this.value.trim() || '';
                }
            });
            return o;
        },
        honDataTable: function (obj) {
            let $this = $(this);
            let result = {
                'language': {
                    "processing": "处理中...",
                    "lengthMenu": "_MENU_ 记录/页",
                    "zeroRecords": "没有匹配的记录",
                    "info": "第 _START_ 至 _END_ 项记录，共 _TOTAL_ 项",
                    "infoEmpty": "第 0 至 0 项记录，共 0 项",
                    "infoFiltered": "(由 _MAX_ 项记录过滤)",
                    "infoPostFix": "",
                    "search": "搜索:",
                    "url": "",
                    "decimal": ",",
                    "thousands": ".",
                    "emptyTable": !!obj.text ? obj.text : "未找到符合条件的数据",
                    "paginate": {
                        "first": "首页",
                        "previous": "上页",
                        "next": "下页",
                        "last": "末页"
                    }
                },
                "destroy": true,
                "columns": obj.columns,
                'paging': obj.paging,
                "processing": true,
                'lengthChange': false,
                'searching': false,
                'ordering': false,
                'info': true,
                'autoWidth': false,
                'deferRender': true
            };
            if (!!obj.columnDefs) {
                result['columnDefs'] = obj.columnDefs;
            }
            if (!!obj.httpRequest) {
                result['ajax'] = obj.httpRequest;
                result['serverSide'] = true;
            }
            if (!!obj.data) {
                result['data'] = obj.data;
            }
            return $this.DataTable(result);
        },
        honTreeTable: function (obj) {
            let $this = $(this);
            return $this.bootstrapTreeTable({
                id: !!obj.id ? obj.id : 'id',                                                   // 选取记录返回的值,用于设置父子关系
                parentId: !!obj.parentId ? obj.parentId : 'parentId',
                type: obj.type,
                url: obj.url,
                ajaxParams: obj.ajaxParams,
                toolbar: null,    //顶部工具条
                expandColumn: !!obj.expandColumn ? obj.expandColumn : 0,            // 在哪一列上面显示展开按钮
                showRefresh: false,
                showColumns: false,
                expandAll: false,                                           // 是否全部展开
                expandFirst: true,
                columns: obj.columns,
                onAll: function (data) {
                    return false;
                },
                onLoadSuccess: obj.loadSuccess,
                onLoadError: function (status) {
                    return false;
                },
                onClickCell: function (field, value, row, $element) {
                    return false;
                },
                onDblClickCell: function (field, value, row, $element) {
                    return false;
                },
                onClickRow: function (row, $element) {
                    return false;
                },
                onDblClickRow: function (row, $element) {
                    return false;
                }
            });
        }
    })
})(jQuery);
// tree下拉选择插件
;(function ($) {
    let defaults = {
        zNodes: [],
        async: {
            enable: false,
            url: "",
            autoParam: ["id"]
        },
        data: {
            key: {
                name: "name",
                isParent: "isParent"
            },
            simpleData: {
                enable: true,
                idKey: "id",
                pIdKey: "parentId",
                rootPId: 0
            }
        },
        callback: {
            onCheck: function (treeSelectObj, treeNode) {
            }
        },
        checks: [],
        blur: function () {
        },
        chkStyle: "checkbox",
        radioType: "all",
        height: 'auto',
        direction: "auto",
        filter: true,
        slideModel: 'click',
        searchShowParent: false,
        textModel: 'detail',/*simple*/
        searchPlaceholder: '按enter检索',
        searchKeys: null,
        containerClass: "",
        chkboxType: {"Y": "ps", "N": "ps"},
        beforeSearchPromise: function (defer, treeSelectObj, keyWord) {
            /*模拟异步加载*/
            defer.resolve();
        },
        renderText: undefined
    };
    var TreeSelect = function (el, options) {
        this.$el = $(el);
        var _this = this;
        if (!Function.prototype.bind) {
            Function.prototype.bind = function (obj) {
                var _self = this
                    , args = arguments;
                return function () {
                    _self.apply(obj, Array.prototype.slice.call(args, 1));
                }
            }
        }
        var $options = function () {
            return $.extend({}, defaults, options, _this.$el.data());//合并业务数据
        };
        /*避免共用同一个配置时两边干扰*/
        this.options = this.cloneObj(new $options());
        this.init();
    };
    TreeSelect.prototype = {
        constructor: TreeSelect,
        init: function () {
            var elChecks = this.$el.attr("checks");
            if (!this.options.checks) {
                this.options.checks = [];
            }
            if (elChecks != "" && elChecks != undefined && elChecks != null) {
                this.options.checks = $.merge(this.options.checks, elChecks.split(','));
            }
            this.render();
            this.initTree();
            this.bindEvent();
        },
        reloadNode: function (nodes) {
            /*纯前台加载*/
            if (nodes) {
                this.options.zNodes = nodes;
            }
            this.initTree();
        },
        getCheckedObject: function () {
            return this.$zTreeObj.getCheckedNodes(true);
        },
        val: function (ids) {
            var nodes = this.$zTreeObj.getCheckedNodes(true);
            var _this = this;

            if (ids == undefined) {
                ids = [];
                $(nodes).each(function (index, node) {
                    ids.push(node[_this.options.data.simpleData.idKey]);
                });
                return ids;
            } else {
                /*赋值*/
                this.options.checks = ids;
                $(nodes).each(function (index, node) {
                    node.checked = false;
                    _this.$zTreeObj.updateNode(node);
                });
                $(ids).each(function (index, id) {
                    var node = _this.$zTreeObj.getNodeByParam("id", id);
                    node.checked = true;
                    _this.$zTreeObj.updateNode(node);
                });
                _this.m2v();
                return this;
            }

        },
        text: function () {
            var nodes = this.$zTreeObj.getCheckedNodes(true);
            var texts = [];
            var _this = this;
            $(nodes).each(function (index, node) {
                var names = [node[_this.options.data.key.name]];
                _this.getNodeName(node, names);
                texts.push(names.join("/"));
            });
            return texts;
        },
        bindEvent: function () {
            if (this.options.slideModel == 'click') {
                this.bindDrawerEventClick();
            } else {
                this.bindDrawerEventSlide();
            }
            if (this.options.filter) {
                this.bindSearch();
            }
        },
        bindSearch: function () {
            this.searchTime = null;
            var _this = this;
            this.clearbtn.click(function () {
                _this.searchInput.val('');
                _this.search_tree_el.hide();
                _this.tree_el.show();
                if (_this.$searchZTreeObj) {
                    _this.$searchZTreeObj.destroy();
                }
                _this.clearbtn.hide();
            });
            this.searchInput.keyup(function (event) {
                var keyWord = $(this).val().trim();
                if (keyWord === "") {
                    _this.clearbtn.hide();
                } else {
                    _this.clearbtn.show();
                }
                if (event.keyCode !== 13) {
                    return false;
                }
                if (_this.options.beforeSearchPromise) {
                    _this.doBeforeSearchPromise(keyWord).then(function () {
                        _this.doSearch(keyWord);
                    });
                } else {
                    _this.doSearch(keyWord);
                }
            });
        },
        doBeforeSearchPromise: function (keyWord) {
            var _this = this;
            return $.Deferred(function (defer) {
                /*进行ajax请求并且加载完成数据后按照下面方式进行调用即可触发搜索*/
                _this.options.beforeSearchPromise(defer, _this, keyWord);
            }).promise();
        },
        doSearch: function (keyWord) {
            var _this = this;
            /*异步执行低频率执行解决性能问题*/
            if (_this.searchTime) {
                clearTimeout(_this.searchTime);
            }
            _this.searchTime = setTimeout(function () {
                if (keyWord === "") {
                    _this.search_tree_el.hide();
                    _this.tree_el.show();
                    if (_this.$searchZTreeObj) {
                        _this.$searchZTreeObj.destroy();
                    }
                    return;
                }
                _this.search_tree_el.show();
                _this.tree_el.hide();

                var nodeList = _this.$zTreeObj.getNodesByFilter(function (node) {
                    return _this.ifHit(node, keyWord);
                });//通过关键字模糊搜索
                /* var nodeList = _this.$zTreeObj.getNodesByParamFuzzy(_this.options.data.key.name, keyWord, null);*/
                var datasMap = {};
                $(nodeList).each(function (index, item) {
                    datasMap[item[_this.options.data.simpleData.idKey]] = item;
                });

                if (_this.options.searchShowParent) {
                    $(nodeList).each(function (index, node) {
                        _this.loadParents(node, datasMap);
                    })
                }
                _this.initSearchTree(Object.values(datasMap), keyWord);
            }, 500);

        },
        ifHit: function (node, keyWord) {
            var _this = this;
            if (_this.options.searchKeys instanceof Array && _this.options.searchKeys.length > 0) {
                var flag = false;
                $(_this.options.searchKeys).each(function (index, item) {
                    if (item.startsWith("like_")) {
                        if (node[item.replace("like_", "")].indexOf(keyWord) != -1) {
                            flag = true;
                            return;
                        }
                    } else if (item.startsWith("eq_")) {
                        if (node[item.replace("eq_", "")] === keyWord) {
                            flag = true;
                            return;
                        }
                    }

                });
                return flag;
            } else {
                return node[_this.options.data.key.name].indexOf(keyWord) != -1;
            }
        },
        loadParents: function (node, datasMap) {
            var parent = node.getParentNode();
            if (parent) {
                var copyparent = $.extend({}, parent);
                copyparent.children = null;
                copyparent.open = true;
                datasMap[parent[this.options.data.simpleData.idKey]] = copyparent;
                this.loadParents(parent, datasMap);
            } else {
                return;
            }
        },
        randomID: function (randomLength) {
            return Number(Math.random().toString().substr(3, randomLength) + new Date().getTime()).toString(36)
        },
        ifSlideUp: function () {
            var _this = this;
            if (_this.options.direction == 'auto') {
                /*元素高度*/
                var elH = _this.$el.height();
                /*元素距离顶部高度*/
                var el2top = _this.$el.offset().top;
                /*下拉框高度*/
                var drowdownHeight = _this.dropdown_container.height();

                /*滚动条位置*/
                var scrollTop = $(document).scrollTop();
                /*下拉框底部距离窗口底部的距离*/
                var height2top = el2top - scrollTop;
                /*窗口高度*/
                var wh = window.innerHeight;
                var height2bottom = wh - el2top - elH;

                if (height2bottom >= drowdownHeight) {
                    return false;
                }
                if (height2top > height2bottom) {
                    return true;
                } else {
                    return false;
                }
            } else if (_this.options.direction != 'up') {
                return false;
            } else {
                return true;
            }

        },
        bindDrawerEventSlide: function () {
            var dropdown_container = this.dropdown_container;
            /*计算抽屉方向*/
            var _this = this;
            this.$el.click(function (event) {
                if (_this.ifSlideUp()) {
                    dropdown_container.addClass("up")
                    dropdown_container.css("bottom", _this.$el.outerHeight());
                    dropdown_container.css("top", '');
                } else {
                    dropdown_container.css("bottom", '');
                    dropdown_container.css("top", _this.$el.outerHeight());
                }
                event.stopPropagation();
                if (!dropdown_container.is(':visible')) {
                    dropdown_container.slideDown("fast");
                } else {
                    dropdown_container.animate({height: 'toggle', opacity: 'toggle'}, "fast");
                }
            });
            this.container.mouseleave(function () {
                if (dropdown_container.is(':visible')) {
                    dropdown_container.animate({height: 'toggle', opacity: 'toggle'}, "fast");
                }
            });

        },
        bindDrawerEventClick: function () {
            var dropdown_container = this.dropdown_container;
            /*计算抽屉方向*/
            var _this = this;
            var onBodyMusedown = function (event) {
                if (!$(event.target).parents("#" + _this.id).length > 0) {
                    dropdown_container.fadeOut("fast");
                    $("body").unbind("mousedown", onBodyMusedown);
                }
            };
            this.$el.click(function () {
                if (_this.ifSlideUp()) {
                    dropdown_container.addClass("up")
                    dropdown_container.css("bottom", _this.$el.outerHeight());
                    dropdown_container.css("top", '');
                } else {
                    dropdown_container.css("bottom", '');
                    dropdown_container.css("top", _this.$el.outerHeight());
                }
                if (!dropdown_container.is(':visible')) {
                    dropdown_container.slideDown("fast");
                    _this.searchInput.focus();
                    $("body").bind("mousedown", onBodyMusedown);
                } else {
                    dropdown_container.fadeOut("fast");
                    $("body").unbind("mousedown", onBodyMusedown);
                }

            });
        },
        render: function () {
            this.$el.css({display: 'block', cursor: 'pointer'});
            this.container = this.$el.wrap('<div class="mts-container ' + this.options.containerClass + '"/>').parent();
            this.searchInput = $('<input class="searchInput" placeholder=' + this.options.searchPlaceholder + ' type="text" style="width: ' + (this.$el.outerWidth() - 20) + 'px;">');
            this.clearbtn = $('<span href="javascript:void(0);" class="clearbtn">×</span>')
            var height = this.options.height + (this.options.height === "auto" ? "" : "px");
            this.tree_el = $('<ul class="ztree" style="height:' + height + '; width:' + (this.$el.outerWidth() - 2) + 'px;"></ul>');
            this.search_tree_el = $('<ul class="ztree" style="height:' + height + '; width:' + (this.$el.outerWidth() - 2) + 'px;"></ul>');

            this.dropdown_container = $('<div  class="dropdown_container"  ></div>');
            if (this.options.filter) {
                this.dropdown_container.append(this.searchInput);
                this.dropdown_container.append(this.clearbtn);
            }
            this.dropdown_container.append(this.tree_el);
            this.dropdown_container.append(this.search_tree_el.hide());
            this.container.append(this.dropdown_container);
            this.id = this.randomID(3);
            this.dropdown_container.attr("id", this.id);
            this.tree_el.attr("id", this.randomID(3));
            this.search_tree_el.attr("id", this.randomID(3));
            return this.container;
        },
        initDefaultCheckedStatus: function (nodes) {
            var _this = this;
            var needUpdateNodes = [];
            if (this.options.checks && this.options.checks.length > 0) {
                $(nodes).each(function (index, node) {
                    var checkFlag = false;
                    $(_this.options.checks).each(function (i, id) {
                        if (id == node[_this.options.data.simpleData.idKey]) {
                            checkFlag = true;
                            if (node.checked != true) {
                                node.checked = true;
                                needUpdateNodes.push(node);
                            }
                            return false;
                        }
                    });
                    if (!checkFlag) {
                        if (node.checked != false) {
                            node.checked = false;
                            needUpdateNodes.push(node)
                        }
                    }
                });
            }
            return needUpdateNodes;
        },
        initTree: function () {
            var _this = this;
            var setting = {
                check: {
                    enable: true,
                    chkboxType: this.options.chkboxType,
                    chkStyle: this.options.chkStyle,
                    radioType: this.options.radioType
                },
                view: {
                    dblClickExpand: false,
                    showIcon: false
                },
                data: _this.options.data,
                callback: {
                    onCheck: _this.onCheck.bind(_this),
                    onAsyncSuccess: _this.onAsyncSuccess.bind(_this),
                    beforeAsync: function (treeId, treeNode) {
                        if (treeNode && treeNode.__notAsync__) {
                            return false;
                        }
                        return true;
                    },
                    onClick: function (e, treeId, treeNode, clickFlag) {
                        _this.$zTreeObj.checkNode(treeNode, !treeNode.checked, true);
                        _this.onCheck(e, treeId, treeNode);
                    }
                },
                async: this.options.async
            };
            /*异步完成后设置初始选中的值*/
            setting.async.dataFilter = function (treeId, parentNode, responseData) {
                if (responseData) {
                    _this.initDefaultCheckedStatus(responseData);
                }
                return responseData;
            };
            _this.initDefaultCheckedStatus(this.options.zNodes);
            this.$zTreeObj = $.fn.zTree.init(this.tree_el, setting, this.options.zNodes);
            this.onCheck(null, _this);
            return this;
        },
        initSearchTree: function (data, param) {
            var _this = this;
            var async = this.options.async;
            if (data.length == 0) {
                async = {
                    enable: false,
                    url: "",
                    autoParam: ["id"]
                }
            }
            var setting = {
                check: {
                    enable: true,
                    chkboxType: this.options.chkboxType,
                    chkStyle: this.options.chkStyle,
                    radioType: this.options.radioType
                },
                view: {
                    dblClickExpand: false,
                    showIcon: false,
                    fontCss: function (treeId, treeNode) {
                        if (_this.ifHit(treeNode, param)) {
                            return {color: "#FFA200", "font-weight": "bold"};
                        }
                        return {}
                    }
                },
                async: async,
                /* 简单树结构会导致默认children为key，从主树上取得的数据带children属性;会导致子节点重复 */
                data: $.extend({}, _this.options.data, {
                    data: {
                        key: {
                            children: "__nodes__"
                        }
                    }
                }),
                callback: {
                    onCheck: function (e, treeId, treeNode) {
                        var node = _this.$zTreeObj.getNodeByParam(_this.options.data.simpleData.idKey, treeNode[_this.options.data.simpleData.idKey]);
                        _this.$zTreeObj.checkNode(node, treeNode.checked, true);
                        _this.onCheck(e, treeId, treeNode);
                    },
                    onClick: function (e, treeId, treeNode, clickFlag) {
                        _this.$searchZTreeObj.checkNode(treeNode, !treeNode.checked, true);
                        _this.$searchZTreeObj.setting.callback.onCheck(e, treeId, treeNode);
                    },
                    onAsyncSuccess: function (event, treeId, treeNode, msg) {
                        /*搜索树节点展开的同时，加载主树响应数据*/
                        if (treeNode) {
                            var node = _this.$zTreeObj.getNodeByParam(_this.options.data.simpleData.idKey, treeNode[_this.options.data.simpleData.idKey]);
                            node.__notAsync__ = true;
                            _this.$zTreeObj.addNodes(node, 0, treeNode.children);
                        }
                    }
                }
            };
            this.$searchZTreeObj = $.fn.zTree.init(this.search_tree_el, setting, data);
        },
        onCheck: function (e, treeId, treeNode) {
            this.m2v();
            this.options.callback.onCheck(this, treeNode);
        },
        checkNode: function (param1, param2, param3) {
            this.$zTreeObj.checkNode(param1, param2, param3);
        },
        getNodeByParam: function (param1, param2) {
            return this.$zTreeObj.getNodeByParam(param1, param2);
        },
        getCheckedNodes: function () {
            return this.$zTreeObj.getCheckedNodes(true);
        },
        m2v: function () {
            var nodes = this.$zTreeObj.getCheckedNodes(true);
            var text = "";
            var checks = "";
            if (this.options.renderText) {
                text = this.options.renderText(nodes);
                for (var i = 0, l = nodes.length; i < l; i++) {
                    checks += nodes[i][this.options.data.simpleData.idKey] + ",";
                }
            } else {
                for (var i = 0, l = nodes.length; i < l; i++) {
                    var texts = [nodes[i][this.options.data.key.name]];
                    this.getNodeName(nodes[i], texts);
                    text += texts.join("/") + ",";
                    checks += nodes[i][this.options.data.simpleData.idKey] + ",";
                }
                if (text.length > 0) {
                    text = text.substring(0, text.length - 1);
                }
            }
            if (checks.length > 0) {
                checks = checks.substring(0, checks.length - 1);
            }
            this.$el.attr("checks", checks);
            this.$el.val(text);
            this.$el.attr("value", "编码:" + checks + ";名称:" + text);
        },
        getNodeName: function (node, texts) {
            if (this.options.textModel == 'simple') {
                return texts;
            }
            var parent = node.getParentNode();
            if (parent) {
                texts.unshift(parent[this.options.data.key.name]);
                return this.getNodeName(parent, texts);
            } else {
                return texts;
            }
        },
        onAsyncSuccess: function (event, treeId, treeNode, msg) {
            this.m2v();
            this.tree_el.find("a").mouseleave(function () {
                event.stopPropagation();
            });
            this.tree_el.find("span").mouseleave(function () {
                event.stopPropagation();
            });
        },
        cloneObj: function (obj) {
            var newObj = {};
            if (obj instanceof Array) {
                newObj = [];
            }
            for (var key in obj) {
                var val = obj[key];
                newObj[key] = typeof val === 'object' ? this.cloneObj(val) : val;
            }
            return newObj;
        },
        destory: function () {
            if (this.container) {
                this.$el.removeData('treeSelect');
                this.$el.val('');
                this.$el.attr("checks", "")
                this.$el.attr("title", "")
                this.container.replaceWith(this.$el);
                this.container = null;
                this.$zTreeObj.destroy();
                if (this.$searchZTreeObj) {
                    this.$searchZTreeObj.destroy();
                }

            }
        }
    };
    $.fn.treeSelect = function (options) {
        var resultArr = [];
        this.each(function () {
            var $this = $(this);
            var treeSelectObj = $this.data('treeSelect');
            if (!treeSelectObj) {
                treeSelectObj = new TreeSelect(this, options);
                $.data(this, 'treeSelect', treeSelectObj);
            }
            resultArr.push(treeSelectObj);
        });
        return resultArr.length === 1 ? resultArr[0] : resultArr;
    };
})(jQuery);
const G = {
    alert: {
        licenseExpired: function () {
            swal({
                title: "提示",
                text: "License已失效，请重新申请",
                buttons: ["确定"]
            }).then(() => {
                window.location.href = "/login";
            });
        },
        timeout: function () {
            swal({
                title: "提示",
                text: "网络连接超时或网络已断开",
                buttons: ["确定"]
            }).then(() => {
                window.location.href = "/login";
            });
        },
        deleteConfirm: function (url, callback) {
            swal({
                title: "确认执行删除操作?",
                text: "一旦删除，数据不可恢复!",
                icon: "warning",
                dangerMode: true,
                buttons: ["取消", "确认"]
            }).then((willDelete) => {
                if (willDelete) {
                    $.ajax({
                        type: 'DELETE',
                        url: url,
                        contentType: 'application/json'
                    }).done(function (e) {
                        G.message.success();
                        callback && callback();
                    }).fail(function (e) {
                        G.message.error(e.responseJSON.message)
                    })
                }
            });
        },
        confirm: function (title, text, callback) {
            swal({
                title: title,
                text: text,
                icon: "warning",
                dangerMode: true,
                buttons: ["取消", "确认"]
            }).then((willDelete) => {
                if (willDelete) {
                    callback && callback(willDelete);
                }
            });
        }
    },
    chart: {
        showLoading: function (echarts, elementId) {
            let element = document.getElementById(elementId);
            let chart = echarts.init(element);
            chart.showLoading({
                text: "图表数据正在努力加载..."
            });
        },
        showNoData: function (echarts, elementId) {
            let element = document.getElementById(elementId);
            let chart = echarts.init(element);
            let msgOption = {
                title: {
                    show: true,
                    textStyle: {
                        color: 'grey',
                        fontSize: 20
                    },
                    text: '暂无数据',
                    left: 'center',
                    top: 'center'
                },
                xAxis: {
                    show: false
                },
                yAxis: {
                    show: false
                },
                series: []
            };
            chart.setOption(msgOption, true);
            chart.hideLoading();
        },
        hideLoading: function (echarts, elementId) {
            let element = document.getElementById(elementId);
            let chart = echarts.init(element);
            chart.hideLoading()
        },
        showChart: function (echarts, elementId, option) {
            let element = document.getElementById(elementId);
            let chart = echarts.init(element);
            chart.setOption(option, true);
            chart.hideLoading();
        },
        initPieOption: function (echarts, obj, unit) {
            if (!unit) {
                unit = ''
            }
            return {
                tooltip: {
                    trigger: 'item',
                    formatter: "{a} <br/> {b}" + unit + " ({d}%)"
                },
                color: [
                    "#1792E5",
                    "#7EB338",
                    "#FFC627",
                    "#F37021",
                    "#EE3124",
                    "#9B5986",
                    "#2a3154",
                    "#e97ccb"
                ],
                legend: {
                    type: 'scroll',
                    orient: 'vertical',
                    left: 'right',
                    bottom: 'center',
                    itemWidth: 10,
                    itemHeight: 10,
                    right: 30,
                    textStyle: {
                        fontSize: '0.9rem',
                    },
                    align: "left",
                    data: obj.datas.map(x => x.name)
                },
                toolbox: {
                    show: false,
                    feature: {
                        mark: {
                            show: true
                        },
                        magicType: {
                            show: true,
                            type: ['pie']
                        },
                        restore: {
                            show: true
                        },
                        saveAsImage: {
                            show: true
                        }
                    }
                },
                series: [{
                    name: obj.seriesName,
                    type: 'pie',
                    avoidLabelOverlap: false,
                    radius: ['70%', '78%'],
                    data: obj.datas,
                    label: {
                        show: true,
                        position: 'center',
                        formatter: obj.labelFormatter,
                        fontSize: '0.9rem'
                    },
                    labelLayout: {
                        hideOverlap: false
                    },
                    labelLine: {
                        show: false
                    },
                    top: 0,
                    bottom: 30,
                    left: 0,
                    right: 200,
                    emphasis: {
                        label: {
                            show: false,
                            fontSize: '16',
                            fontWeight: 'bold'
                        }
                    }
                }]
            };
        },
        initLineAndBarOption: function (echarts, obj, stackable, unit) {
            let result = {
                title: {
                    text: !!obj.text ? obj.text : "",
                    textStyle: {
                        fontSize: '0.8rem',
                        fontWeight: "bold"
                    }
                },
                tooltip: {
                    trigger: 'axis',
                    axisPointer: {
                        type: 'cross'
                    }
                },
                color: !!obj.color ? obj.color : [
                    "#1792E5",
                    "#7EB338",
                    "#FFC627",
                    "#F37021",
                    "#EE3124",
                    "#9B5986",
                    "#73bfdd",
                    "#5470c5"
                ],
                toolbox: {
                    show: false,
                    feature: {
                        restore: {
                            show: true
                        },
                        saveAsImage: {
                            show: true
                        }
                    }
                },
                legend: {
                    left: 'right',
                    itemWidth: 10,
                    itemHeight: 10,
                    data: !!obj.datas ? obj.datas.map(x => x.name) : []
                },
                grid: {
                    left: 20,
                    right: 20,
                    bottom: 10,
                    containLabel: true
                },
                yAxis: [{
                    name: `单位（${unit}）`,
                    type: 'value'
                }],
                xAxis: [{
                    type: 'category',
                    data: obj.xData,
                    axisTick: { // 轴标记
                        alignWithLabel: true
                    },
                    axisLabel: {
                        formatter: '{value} '
                    }
                }],
                series: obj.datas.map(x => {
                    let obj = {};
                    obj.name = x.name;
                    obj.type = x.type;
                    if (x.type === 'line') {
                        obj.smooth = true;
                    } else if (x.type === 'bar') {
                        obj.barMaxWidth = 8;
                        if (stackable) {
                            obj.stack = '0'
                        }
                    }
                    if (!!x.yAxisIndex) {
                        obj.yAxisIndex = x.yAxisIndex;
                    }
                    obj.data = x.datas;
                    return obj;
                })
            };
            if (!!obj.formatter) {
                result.tooltip.formatter = obj.formatter;
            }
            if (!!obj.yAxis) {
                result.yAxis = obj.yAxis;
            }
            return result;
        },
        initBarOption: function (echarts, obj, stackable, unit, horizontal) {
            let xAxis, yAxis, result;
            if (!unit) {
                unit = ''
            }
            if (!horizontal) {
                xAxis = [{
                    type: 'category',
                    data: obj.xData,
                    axisTick: { // 轴标记
                        alignWithLabel: true
                    },
                    axisLabel: {
                        formatter: '{value} '
                    }
                }];
                yAxis = [{
                    type: 'value',
                    name: `单位（${unit}）`
                }];
            } else {
                xAxis = [{
                    show: false,
                    type: 'value',
                    name: `单位（${unit}）`,
                    max: 'dataMax'
                }];
                yAxis = [{
                    type: 'category',
                    data: obj.xData,
                    axisTick: { // 轴标记
                        alignWithLabel: true
                    },
                    axisLabel: {
                        formatter: '{value} '
                    }
                }];
            }
            result = {
                title: {
                    text: !!obj.text ? obj.text : "",
                    subtext: !!obj.subtext ? obj.subtext : "",
                    textStyle: {
                        fontSize: '0.8rem',
                        fontWeight: "bold"
                    }
                },
                tooltip: {
                    trigger: 'axis'
                    // axisPointer: _axisPointer
                },
                color: [
                    "#1792E5",
                    "#7EB338",
                    "#FFC627",
                    "#F37021",
                    "#EE3124",
                    "#9B5986",
                    "#73bfdd",
                    "#5470c5"
                ],
                toolbox: {
                    show: false,
                    feature: {
                        restore: {
                            show: true
                        },
                        saveAsImage: {
                            show: true
                        }
                    }
                },
                grid: {
                    left: horizontal ? 0 : 20,
                    right: horizontal ? 80 : 20,
                    bottom: 10,
                    top: horizontal ? 0 : 60,
                    containLabel: true
                },
                legend: {
                    left: 'right',
                    itemWidth: 10,
                    itemHeight: 10,
                    data: !!obj.datas ? obj.datas.map(x => x.name) : []
                },
                xAxis: xAxis,
                yAxis: yAxis,
                series: !!obj.datas ? obj.datas.map(x => {
                    x.type = 'bar';
                    if (stackable) {
                        x.stack = '0';
                    }
                    x.barMaxWidth = 8;
                    x.data = x.datas;
                    return x;
                }) : []
            };
            if (!!obj.formatter) {
                result.tooltip.formatter = obj.formatter;
            }
            return result;
        },
        initLineOption: function (echarts, obj, unit, showLabel) {
            if (!unit) {
                unit = ''
            }
            let result = {
                title: {
                    text: !!obj.text ? obj.text : "",
                    textStyle: {
                        fontSize: '0.8rem',
                        fontWeight: "bold"
                    }
                },
                tooltip: {
                    trigger: 'axis'
                    // axisPointer: _axisPointer
                },
                color: [
                    "#1792E5",
                    "#7EB338",
                    "#FFC627",
                    "#F37021",
                    "#EE3124",
                    "#9B5986",
                    "#73bfdd",
                    "#5470c5"
                ],
                legend: {
                    left: 'right',
                    itemWidth: 10,
                    itemHeight: 10,
                    data: !!obj.datas ? obj.datas.map(x => x.name) : []
                },
                grid: {
                    left: 20,
                    right: 20,
                    bottom: showLabel ? 50 : 10,
                    containLabel: true
                },
                toolbox: {
                    show: false,
                    feature: {
                        mark: {
                            show: true
                        },
                        magicType: {
                            show: true,
                            type: []
                        },
                        restore: {
                            show: true
                        },
                        saveAsImage: {
                            show: true
                        }
                    }
                },
                calculable: true,
                xAxis: [{
                    type: 'category',
                    data: obj.xData
                }],
                yAxis: [{
                    name: `单位（${unit}）`,
                    type: 'value'
                }],
                dataZoom: [{
                    show: showLabel,
                    realtime: showLabel,
                    start: 0,
                    end: 100,
                    xAxisIndex: [0]
                }],
                series: !!obj.datas ? obj.datas.map(x => {
                    let temp = {}
                    temp.name = x.name
                    temp.type = 'line';
                    temp.data = x.datas;
                    temp.smooth = true;
                    return temp;
                }) : []
            }
            if (!!obj.formatter) {
                result.tooltip.formatter = obj.formatter;
            }
            return result;
        },
        initGaugeOption: function (echarts, obj) {
            return {
                series: [{
                    type: 'gauge',
                    startAngle: 180,
                    endAngle: 0,
                    min: obj.min,
                    max: obj.max,
                    splitNumber: -1,
                    axisLine: {
                        lineStyle: {
                            width: 8,
                            color: [
                                [0.33, '#F37021'],
                                [0.66, '#FFC627'],
                                [1, '#7EB338']
                            ]
                        }
                    },
                    pointer: {
                        icon: 'path://M12.8,0.7l12,40.1H0.7L12.8,0.7z',
                        length: '12%',
                        width: "10rem",
                        offsetCenter: [0, '-60%'],
                        itemStyle: {
                            color: 'auto'
                        }
                    },
                    axisTick: {
                        length: 12,
                        lineStyle: {
                            color: 'auto',
                            width: 2
                        }
                    },
                    splitLine: {
                        length: 20,
                        lineStyle: {
                            color: 'auto',
                            width: 5
                        }
                    },
                    axisLabel: {
                        color: '#464646',
                        fontSize: 20,
                        distance: -60,
                        formatter: obj.formatter
                    },
                    title: {
                        offsetCenter: [0, '50%'],
                        fontSize: 14
                    },
                    detail: {
                        fontSize: "2rem",
                        offsetCenter: [0, '0%'],
                        valueAnimation: true,
                        formatter: obj.detailFormatter,
                        color: 'auto'
                    },
                    data: obj.data
                }]
            }
        },
        initSankeyOption: function (echarts, obj) {
            return {
                tooltip: {
                    trigger: 'item',
                    triggerOn: 'mousemove'
                },
                series: [
                    {
                        type: 'sankey',
                        emphasis: {
                            focus: 'adjacency'
                        },
                        lineStyle: {
                            curveness: 0.5
                        },
                        data: obj.data,
                        links: obj.links
                    }
                ]
            }
        },
    },
    date: {
        monthPicker: function (elementId, obj) {
            let tempObj = {
                singleDatePicker: true,
                calendarMode: "months",
                startView: 'months',
                minDate: moment().subtract(5, 'years'),
                locale: {
                    format: 'MM/YYYY',
                    applyLabel: '确定',
                    cancelLabel: '取消',
                    fromLabel: '开始日期',
                    toLabel: '结束日期',
                    customRangeLabel: '自定义日期',
                    daysOfWeek: ['日', '一', '二', '三', '四', '五', '六'],
                    monthNames: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
                    firstDay: 1
                }
            }
            $('#' + elementId).daterangepicker($.extend(tempObj, obj));
        },
        datePicker: function (elementId, obj) {
            let tempObj = {
                "singleDatePicker": true,
                "showDropdowns": true,
                "startDate": !!obj && !!obj.startDate ? obj.startDate : moment(),
                "locale": {
                    "format": "MM/DD/YYYY",
                    "separator": " - ",
                    "applyLabel": "确认",
                    "cancelLabel": "取消",
                    "fromLabel": "开始",
                    "toLabel": "结束",
                    "customRangeLabel": "自定义",
                    "weekLabel": "周",
                    "daysOfWeek": [
                        "日",
                        "一",
                        "二",
                        "三",
                        "四",
                        "五",
                        "六"
                    ],
                    "monthNames": [
                        "一月",
                        "二月",
                        "三月",
                        "四月",
                        "五月",
                        "六月",
                        "七月",
                        "八月",
                        "九月",
                        "十月",
                        "十一月",
                        "十二月"
                    ],
                    "firstDay": 1
                }
            }
            $('#' + elementId).daterangepicker(tempObj);
        },
        rangePicker: function (elementId, obj) {
            let tempObj = {
                "showDropdowns": true,
                "linkedCalendars": false,
                "autoUpdateInput": true,
                "showCustomRangeLabel": false,
                "opens": "center",
                "minDate": moment().subtract(5, 'years'),
                "dateLimit": {
                    "years": 5
                },
                "locale": {
                    "format": "MM/DD/YYYY",
                    "separator": " - ",
                    "applyLabel": "确认",
                    "cancelLabel": "取消",
                    "fromLabel": "开始",
                    "toLabel": "结束",
                    "customRangeLabel": "自定义",
                    "weekLabel": "周",
                    "daysOfWeek": [
                        "日",
                        "一",
                        "二",
                        "三",
                        "四",
                        "五",
                        "六"
                    ],
                    "monthNames": [
                        "一月",
                        "二月",
                        "三月",
                        "四月",
                        "五月",
                        "六月",
                        "七月",
                        "八月",
                        "九月",
                        "十月",
                        "十一月",
                        "十二月"
                    ],
                    "firstDay": 1
                }
            };
            $('#' + elementId).daterangepicker($.extend(tempObj, obj));
        },
        freshShowTime: function (element) {
            let headerTime = {

                prefix0: function (v) {
                    if (v >= 0 && v <= 9) {
                        v = "0" + v;
                    }
                    return v;
                },

                getFormatWeek: function (day) {
                    let week;
                    switch (day) {
                        case 1:
                            week = "星期一";
                            break;
                        case 2:
                            week = "星期二";
                            break;
                        case 3:
                            week = "星期三";
                            break;
                        case 4:
                            week = "星期四";
                            break;
                        case 5:
                            week = "星期五";
                            break;
                        case 6:
                            week = "星期六";
                            break;
                        default:
                            week = "星期日";
                    }
                    return week;
                },

                showHeaderFormatDate: function (element) {
                    let _this = this;
                    let _date = new Date();
                    let year = _date.getFullYear();
                    let month = _date.getMonth() + 1;
                    let day = _date.getDate();
                    let weekDay = _date.getDay();
                    let hour = _date.getHours();
                    let minute = _date.getMinutes();
                    let sec = _date.getSeconds();

                    let seperator1 = '.';
                    let seperator2 = ':';

                    let currentdate = year + seperator1 + _this.prefix0(month) + seperator1 + _this.prefix0(day) +
                        " " + _this.getFormatWeek(weekDay) + " " + _this.prefix0(hour) + seperator2 + _this.prefix0(minute) + seperator2 + _this.prefix0(sec);

                    $(element).text(currentdate);
                }
            };

            window.setInterval(function () {
                headerTime.showHeaderFormatDate(element)
            }, 1000);
        }
    },
    multiselect: {
        select: function (elementId) {
            let locales = {
                nonSelectedText: '未选择',
                selectAllText: '全部选中',
                allSelectedText: '全部选中',
                nSelectedText: '个项目已选中'
            }

            $(elementId).multiselect('destroy').multiselect($.extend({
                buttonWidth: '100%',
                includeSelectAllOption: true
            }, locales));
        }
    },
    message: {
        success: function (message) {
            $.toast({
                text: !!message ? message : "", // Text that is to be shown in the toast
                heading: '操作成功！', // Optional heading to be shown on the toast
                icon: 'success', // Type of toast icon
                showHideTransition: 'fade', // fade, slide or plain
                allowToastClose: true, // Boolean value true or false
                hideAfter: 5000, // false to make it sticky or number representing the miliseconds as time after which toast needs to be hidden
                stack: false, // false if there should be only one toast at a time or a number representing the maximum number of toasts to be shown at a time
                position: 'bottom-right', // bottom-left or bottom-right or bottom-center or top-left or top-right or top-center or mid-center or an object representing the left, right, top, bottom values
                textAlign: 'left',  // Text alignment i.e. left, right or center
                loader: false,  // Whether to show loader or not. True by default
                loaderBg: '#9EC600',  // Background color of the toast loader
                beforeShow: function () {
                }, // will be triggered before the toast is shown
                afterShown: function () {
                }, // will be triggered after the toat has been shown
                beforeHide: function () {
                }, // will be triggered before the toast gets hidden
                afterHidden: function () {
                }  // will be triggered after the toast has been hidden
            });
        },
        error: function (message) {
            $.toast({
                text: !!message ? message : "", // Text that is to be shown in the toast
                heading: '操作失败！', // Optional heading to be shown on the toast
                icon: 'error', // Type of toast icon
                showHideTransition: 'fade', // fade, slide or plain
                allowToastClose: true, // Boolean value true or false
                hideAfter: 3000, // false to make it sticky or number representing the miliseconds as time after which toast needs to be hidden
                stack: false, // false if there should be only one toast at a time or a number representing the maximum number of toasts to be shown at a time
                position: 'bottom-right', // bottom-left or bottom-right or bottom-center or top-left or top-right or top-center or mid-center or an object representing the left, right, top, bottom values
                textAlign: 'left',  // Text alignment i.e. left, right or center
                loader: false,  // Whether to show loader or not. True by default
                loaderBg: '#9EC600',  // Background color of the toast loader
                beforeShow: function () {
                }, // will be triggered before the toast is shown
                afterShown: function () {
                }, // will be triggered after the toat has been shown
                beforeHide: function () {
                }, // will be triggered before the toast gets hidden
                afterHidden: function () {
                }  // will be triggered after the toast has been hidden
            });
        },
        warning: function (message) {
            $.toast({
                text: !!message ? message : "", // Text that is to be shown in the toast
                heading: '提示！', // Optional heading to be shown on the toast
                icon: 'warning', // Type of toast icon
                showHideTransition: 'fade', // fade, slide or plain
                allowToastClose: true, // Boolean value true or false
                hideAfter: 3000, // false to make it sticky or number representing the miliseconds as time after which toast needs to be hidden
                stack: true, // false if there should be only one toast at a time or a number representing the maximum number of toasts to be shown at a time
                position: 'bottom-right', // bottom-left or bottom-right or bottom-center or top-left or top-right or top-center or mid-center or an object representing the left, right, top, bottom values
                textAlign: 'left',  // Text alignment i.e. left, right or center
                loader: false,  // Whether to show loader or not. True by default
                loaderBg: '#9EC600',  // Background color of the toast loader
                beforeShow: function () {
                }, // will be triggered before the toast is shown
                afterShown: function () {
                }, // will be triggered after the toat has been shown
                beforeHide: function () {
                }, // will be triggered before the toast gets hidden
                afterHidden: function () {
                }  // will be triggered after the toast has been hidden
            });
        },
        alarm: function (message) {
            $.toast({
                text: !!message ? message : "", // Text that is to be shown in the toast
                heading: '报警信息！', // Optional heading to be shown on the toast
                icon: 'error', // Type of toast icon
                showHideTransition: 'slide', // fade, slide or plain
                allowToastClose: true, // Boolean value true or false
                hideAfter: 3000, // false to make it sticky or number representing the miliseconds as time after which toast needs to be hidden
                stack: false, // false if there should be only one toast at a time or a number representing the maximum number of toasts to be shown at a time
                position: 'bottom-right', // bottom-left or bottom-right or bottom-center or top-left or top-right or top-center or mid-center or an object representing the left, right, top, bottom values
                textAlign: 'left',  // Text alignment i.e. left, right or center
                loader: false,  // Whether to show loader or not. True by default
                loaderBg: '#9EC600',  // Background color of the toast loader
                beforeShow: function () {
                }, // will be triggered before the toast is shown
                afterShown: function () {
                }, // will be triggered after the toat has been shown
                beforeHide: function () {
                }, // will be triggered before the toast gets hidden
                afterHidden: function () {
                }  // will be triggered after the toast has been hidden
            });
        },
    },
    time: {
        processTime: function (startTime, endTime, timeAggregation) {
            let start = startTime;
            let end = endTime;
            let time = [];
            if (timeAggregation === 0) {
                let size = end.diff(start, 'minutes')
                for (let i = 0; i <= size; i = i + 15) {
                    time.push(start.add(i, 'minutes').format("YYYY-MM-DD HH:mm:ss"))
                }
            } else if (timeAggregation === 1) {
                let size = end.diff(start, 'days')
                for (let i = 0; i <= size; i = i + 1) {
                    time.push(start.add(i, 'days').format("YYYY-MM-DD HH:mm:ss"))
                }
            } else if (timeAggregation === 2) {
                let size = end.diff(start, 'weeks')
                for (let i = 0; i <= size; i = i + 1) {
                    time.push(start.add(i, 'weeks').format("YYYY-MM-DD HH:mm:ss"))
                }
            } else if (timeAggregation === 3) {
                let size = end.diff(start, 'months')
                for (let i = 0; i <= size; i = i + 1) {
                    time.push(start.add(i, 'months').format("YYYY-MM-DD HH:mm:ss"))
                }
            }
            return time;
        }
    },
    password: {
        loadKey: function (salt) {
            let initData = {
                rawKey: salt,
                hash: "SHA-256",
                algoName: "AES-GCM",
                algoLength: 256
            }
            return window.crypto.subtle.importKey(
                "raw",
                window.base64js.toByteArray(initData.rawKey),
                {
                    name: initData.algoName,
                    length: initData.algoLength
                },
                true,
                ["encrypt", "decrypt"]
            )
        },
        encrypt: function (key, data) {
            //IV is supposed to be 12 bytes
            let encodedText = new TextEncoder().encode(data);
            let iv = new Uint8Array([-15, 34, -58, 25, -69, -69, 33, -123, -43, -57, -128, -55]);
            return window.crypto.subtle.encrypt({
                    name: "AES-GCM",
                    iv: iv
                },
                key,
                encodedText)
                .then(
                    (encryptedData) => {
                        let encryptedBytes = new Uint8Array(encryptedData);
                        let encryptedBase64 = window.base64js.fromByteArray(encryptedBytes);
                        return window.base64js.fromByteArray(iv) + "." + encryptedBase64;
                    },
                    (error) => {
                        throw error;
                    });
        },
        decrypt: function (key, data) {
            let b64Iv = data.split(".")[0]
            let b64Data = data.split(".")[1];
            let iv = window.base64js.toByteArray(b64Iv);
            let encryptedBytes = window.base64js.toByteArray(b64Data);

            return window.crypto.subtle.decrypt({
                    name: "AES-GCM",
                    iv: iv
                },
                key,
                encryptedBytes)
                .then(
                    (decryptedData) => {
                        let decryptedBytes = new Uint8Array(decryptedData);
                        return new TextDecoder("utf-8").decode(decryptedBytes);
                    },
                    (error) => {
                        throw error;
                    }
                )
        }
    }

}
